/************************************************************************
                                                                     
   Xceed Ultimate ListBox for Silverlight                                                                                                                                            
   Copyright (C) 2010 Xceed Software Inc.    
                                                                     
   This program is provided to you under the terms of the GNU General Public  
   License version 2 as published by the Free Software Foundation. 
        
   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY, without even the implied warranty of 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along 
   with this program, if not, write to the Free Software Foundation, Inc., 
   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

   This program can be provided to you by Xceed Software Inc. under a
   proprietary commercial license agreement for use in non-Open Source
   projects. Visit Xceed.com to find the commercial edition of 
   Xceed Ultimate ListBox for Silverlight.                                    
                                                                      
 **********************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using Xceed.Silverlight.Data.Stats;

namespace Xceed.Silverlight.Data
{
  public abstract class SimpleSyncIndexDataSourceProvider : SyncIndexDataSourceProvider
  {
    #region Constructor

    protected SimpleSyncIndexDataSourceProvider()
      : base()
    {
      this.InitializeFeatures();
    }

    #endregion

    #region CanFilterGroup Property

    public override bool CanFilterGroup
    {
      get
      {
        return m_canFilterGroup;
      }
    }

    private bool m_canFilterGroup; //false

    #endregion

    #region CanFind Property

    public override bool CanFind
    {
      get
      {
        return m_canFind;
      }
    }

    private bool m_canFind; //false

    #endregion

    #region CanGroupCount Property

    public override bool CanGroupCount
    {
      get
      {
        return m_canGroupCount;
      }
    }

    private bool m_canGroupCount; //false

    #endregion

    #region CanCalculateStats Property

    public override bool CanCalculateStats
    {
      get
      {
        return m_canCalculateStats;
      }
    }

    private bool m_canCalculateStats; //false

    #endregion

    #region Count Protected Property

    protected abstract long Count
    {
      get;
    }

    #endregion

    protected virtual long GetCount( IEnumerable<FieldValue> fieldValues )
    {
      throw new NotSupportedException();
    }

    protected abstract IEnumerable GetRange( long index, int count );

    protected abstract IEnumerable GetRange( IEnumerable<SortDescription> sortOrder, long index, int count );

    protected virtual IEnumerable<long> GetGroupCount( IEnumerable<SortDescription> sortOrder, long index, int count )
    {
      throw new NotSupportedException();
    }

    protected virtual IEnumerable<long> GetGroupCount( IEnumerable<SortDescription> sortOrder, IEnumerable<FieldValue> fieldValues, long index, int count )
    {
      throw new NotSupportedException();
    }

    protected virtual StatResult GetStatResult( StatFunction statFunction )
    {
      throw new NotSupportedException();
    }

    protected virtual StatResult GetStatResult( StatFunction statFunction, IEnumerable<FieldValue> fieldValues )
    {
      throw new NotSupportedException();
    }

    protected virtual FindResult Find( IEnumerable<SortDescription> sortOrder, IEnumerable<FindValue> fieldValues, FindPosition position )
    {
      throw new NotSupportedException();
    }

    protected sealed override CountQueryResult ExecuteCountQuery( CountQueryParameters queryParameters )
    {
      long count;

      if( queryParameters.FilterGroup != null )
      {
        IEnumerable<FieldValue> fieldValues = this.GetFieldValues(
                                                queryParameters.Sort.SortDescriptions,
                                                queryParameters.FilterGroup.Values ).ToList();

        count = this.GetCount( fieldValues );
      }
      else
      {
        count = this.Count;
      }

      return new CountQueryResult( count );
    }

    protected sealed override DataQueryResult ExecuteDataQuery( DataQueryParameters queryParameters )
    {
      // Retrieve the query parameters.
      IEnumerable<SortDescription> sortOrder = ( queryParameters.Sort != null ) ? queryParameters.Sort.SortDescriptions : null;
      long index = ( queryParameters.Skip != null ) ? queryParameters.Skip.Count : 0;
      int count = ( queryParameters.Take != null ) ? queryParameters.Take.Count : int.MaxValue;
      IEnumerable dataList;

      // Retrieve the requested data items.
      if( sortOrder != null )
      {
        dataList = this.GetRange( sortOrder, index, count );
      }
      else
      {
        dataList = this.GetRange( index, count );
      }

      long totalCount = this.Count;

      return new DataQueryResult( dataList, null, totalCount );
    }

    protected sealed override FindQueryResult ExecuteFindQuery( FindQueryParameters queryParameters )
    {
      Debug.Assert( ( queryParameters.Sort != null ) && ( queryParameters.Find != null ) );

      // Retrieve the query parameters.
      IEnumerable<SortDescription> sortOrder = queryParameters.Sort.SortDescriptions;
      IEnumerable<FindValue> fieldValues = queryParameters.Find.Values;
      FindPosition position = queryParameters.Find.Position;

      FindResult result = this.Find( sortOrder, fieldValues, position );

      return new FindQueryResult( result );
    }

    protected sealed override GroupCountQueryResult ExecuteGroupCountQuery( GroupCountQueryParameters queryParameters )
    {
      IEnumerable<SortDescription> sortOrder = queryParameters.Sort.SortDescriptions;
      IEnumerable<long> groupCount;
      long index = ( queryParameters.Skip != null ) ? queryParameters.Skip.Count : 0;
      int length = queryParameters.Take.Count;

      if( queryParameters.FilterGroup != null )
      {
        IEnumerable<FieldValue> fieldValues = this.GetFieldValues(
                                                queryParameters.Sort.SortDescriptions,
                                                queryParameters.FilterGroup.Values ).ToList();

        groupCount = this.GetGroupCount( sortOrder, fieldValues, index, length );
      }
      else
      {
        groupCount = this.GetGroupCount( sortOrder, index, length );
      }

      return new GroupCountQueryResult( groupCount );
    }

    protected sealed override StatQueryResult ExecuteStatQuery( StatQueryParameters queryParameters )
    {
      StatFunction statFunction = queryParameters.Function.StatFunction;
      StatResult statResult;

      if( queryParameters.FilterGroup != null )
      {
        IEnumerable<FieldValue> fieldValues = this.GetFieldValues(
                                                queryParameters.Sort.SortDescriptions,
                                                queryParameters.FilterGroup.Values ).ToList();

        statResult = this.GetStatResult( statFunction, fieldValues );
      }
      else
      {
        statResult = this.GetStatResult( statFunction );
      }

      return new StatQueryResult( statResult );
    }

    private IEnumerable<FieldValue> GetFieldValues(
      IEnumerable<SortDescription> sortOrder,
      IEnumerable<FilterGroupValue> values )
    {
      return sortOrder.Zip( values, ( sort, value ) => new FieldValue( sort.PropertyName, value.Value ) );
    }

    private void InitializeFeatures()
    {
      Type dataSourceProviderType = this.GetType();
      bool groupLevelCountImplemented = DataUtils.IsMethodImplemented(
                                          DataUtils.GetMethodDefinition<IEnumerable<FieldValue>, long>(
                                            ( fieldValues ) => this.GetCount( fieldValues ) ),
                                          dataSourceProviderType );
      bool allLevelGroupCountImplemented = DataUtils.IsMethodImplemented(
                                             DataUtils.GetMethodDefinition<IEnumerable<SortDescription>, long, int, IEnumerable<long>>(
                                               ( sortOrder, index, count ) => this.GetGroupCount( sortOrder, index, count ) ),
                                             dataSourceProviderType );
      bool groupLevelGroupCountImplemented = DataUtils.IsMethodImplemented(
                                               DataUtils.GetMethodDefinition<IEnumerable<SortDescription>, IEnumerable<FieldValue>, long, int, IEnumerable<long>>(
                                                 ( sortOrder, fieldValues, index, count ) => this.GetGroupCount( sortOrder, fieldValues, index, count ) ),
                                               dataSourceProviderType );
      bool allLevelStatImplemented = DataUtils.IsMethodImplemented(
                                       DataUtils.GetMethodDefinition<StatFunction, StatResult>(
                                         ( statFunction ) => this.GetStatResult( statFunction ) ),
                                       dataSourceProviderType );
      bool groupLevelStatImplemented = DataUtils.IsMethodImplemented(
                                         DataUtils.GetMethodDefinition<StatFunction, IEnumerable<FieldValue>, StatResult>(
                                           ( statFunction, fieldValues ) => this.GetStatResult( statFunction, fieldValues ) ),
                                         dataSourceProviderType );
      bool findImplemented = DataUtils.IsMethodImplemented(
                               DataUtils.GetMethodDefinition<IEnumerable<SortDescription>, IEnumerable<FindValue>, FindPosition, FindResult>(
                                 ( sortOrder, findValues, position ) => this.Find( sortOrder, findValues, position ) ),
                               dataSourceProviderType );

      m_canFind = findImplemented;
      m_canGroupCount = m_canFind && allLevelGroupCountImplemented;
      m_canCalculateStats = allLevelStatImplemented;

      m_canFilterGroup = ( groupLevelCountImplemented )
                      && ( !m_canGroupCount || groupLevelGroupCountImplemented )
                      && ( !m_canCalculateStats || groupLevelStatImplemented );
    }
  }
}
